﻿//------------------------------------------------------------------------------
//  此代码版权声明为全文件覆盖，如有原作者特别声明，会在下方手动补充
//  此代码版权（除特别声明外的代码）归作者本人Diego所有
//  源代码使用协议遵循本仓库的开源协议及附加协议
//  Gitee源代码仓库：https://gitee.com/diego2098/ThingsGateway
//  Github源代码仓库：https://github.com/kimdiego2098/ThingsGateway
//  使用文档：https://kimdiego2098.github.io/
//  QQ群：605534569
//------------------------------------------------------------------------------

using System.Runtime.CompilerServices;
using System.Security.Cryptography;

namespace ThingsGateway.NewLife.X.Security;

/// <summary>高性能低碰撞Murmur128哈希算法</summary>
/// <remarks>
/// Redis等大量使用，比MD5要好
/// </remarks>
public class Murmur128 : HashAlgorithm
{
    #region 属性

    private const UInt64 C1 = 0x87c37b91114253d5;
    private const UInt64 C2 = 0x4cf5ad432745937f;

    private readonly UInt32 _Seed;

    private UInt64 _H1;

    private UInt64 _H2;

    private Int32 _Length;

    /// <summary>哈希大小</summary>
    public override Int32 HashSize => 128;

    /// <summary>种子</summary>
    public UInt32 Seed => _Seed;

    #endregion 属性

    #region 构造

    /// <summary>实例化</summary>
    /// <param name="seed"></param>
    public Murmur128(UInt32 seed = 0)
    {
        _Seed = seed;
        Reset();
    }

    #endregion 构造

    #region 方法

    /// <summary>初始化</summary>
    public override void Initialize() => Reset();

    /// <summary>哈希核心</summary>
    /// <param name="array"></param>
    /// <param name="ibStart"></param>
    /// <param name="cbSize"></param>
    protected override void HashCore(Byte[] array, Int32 ibStart, Int32 cbSize)
    {
        // 增加长度
        _Length += cbSize;
        Body(array, ibStart, cbSize);
    }

    /// <summary>哈希结束</summary>
    /// <returns></returns>
    protected override Byte[] HashFinal()
    {
        var len = (UInt64)_Length;
        _H1 ^= len; _H2 ^= len;

        _H1 += _H2;
        _H2 += _H1;

        _H1 = FMix(_H1);
        _H2 = FMix(_H2);

        _H1 += _H2;
        _H2 += _H1;

        var result = new Byte[16];
        Array.Copy(BitConverter.GetBytes(_H1), 0, result, 0, 8);
        Array.Copy(BitConverter.GetBytes(_H2), 0, result, 8, 8);

        return result;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void Body(Byte[] data, Int32 start, Int32 length)
    {
        var remainder = length & 15;
        var alignedLength = start + (length - remainder);
        for (var i = start; i < alignedLength; i += 16)
        {
            _H1 ^= RotateLeft(data.ToUInt64(i) * C1, 31) * C2;
            _H1 = (RotateLeft(_H1, 27) + _H2) * 5 + 0x52dce729;

            _H2 ^= RotateLeft(data.ToUInt64(i + 8) * C2, 33) * C1;
            _H2 = (RotateLeft(_H2, 31) + _H1) * 5 + 0x38495ab5;
        }

        if (remainder > 0)
            Tail(data, alignedLength, remainder);
    }

    private void Reset()
    {
        // 初始化哈希值到种子
        _H1 = _H2 = Seed;

        // 重置长度为0
        _Length = 0;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void Tail(Byte[] tail, Int32 start, Int32 remaining)
    {
        // create our keys and initialize to 0
        UInt64 k1 = 0, k2 = 0;

        // determine how many bytes we have left to work with based on length
        switch (remaining)
        {
            case 15: k2 ^= (UInt64)tail[start + 14] << 48; goto case 14;
            case 14: k2 ^= (UInt64)tail[start + 13] << 40; goto case 13;
            case 13: k2 ^= (UInt64)tail[start + 12] << 32; goto case 12;
            case 12: k2 ^= (UInt64)tail[start + 11] << 24; goto case 11;
            case 11: k2 ^= (UInt64)tail[start + 10] << 16; goto case 10;
            case 10: k2 ^= (UInt64)tail[start + 9] << 8; goto case 9;
            case 9: k2 ^= (UInt64)tail[start + 8] << 0; goto case 8;
            case 8: k1 ^= (UInt64)tail[start + 7] << 56; goto case 7;
            case 7: k1 ^= (UInt64)tail[start + 6] << 48; goto case 6;
            case 6: k1 ^= (UInt64)tail[start + 5] << 40; goto case 5;
            case 5: k1 ^= (UInt64)tail[start + 4] << 32; goto case 4;
            case 4: k1 ^= (UInt64)tail[start + 3] << 24; goto case 3;
            case 3: k1 ^= (UInt64)tail[start + 2] << 16; goto case 2;
            case 2: k1 ^= (UInt64)tail[start + 1] << 8; goto case 1;
            case 1: k1 ^= (UInt64)tail[start] << 0; break;
        }

        _H2 ^= RotateLeft(k2 * C2, 33) * C1;
        _H1 ^= RotateLeft(k1 * C1, 31) * C2;
    }

    #endregion 方法

    #region 辅助

    //[MethodImpl(MethodImplOptions.AggressiveInlining)]
    //private static UInt32 RotateLeft(UInt32 x, Byte r) => (x << r) | (x >> (32 - r));

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static UInt64 FMix(UInt64 h)
    {
        h = (h ^ (h >> 33)) * 0xff51afd7ed558ccd;
        h = (h ^ (h >> 33)) * 0xc4ceb9fe1a85ec53;

        return (h ^ (h >> 33));
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static UInt64 RotateLeft(UInt64 x, Byte r) => (x << r) | (x >> (64 - r));

    //[MethodImpl(MethodImplOptions.AggressiveInlining)]
    //private static UInt32 FMix(UInt32 h)
    //{
    //    h = (h ^ (h >> 16)) * 0x85ebca6b;
    //    h = (h ^ (h >> 13)) * 0xc2b2ae35;
    //    return h ^ (h >> 16);
    //}

    #endregion 辅助
}
